// 版权归 瞄你个汪 所有。使用本代码应遵守相关法律法规和Apache 2.0开源许可要求。
package top.geeke.toolkit;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.logging.log4j.util.Strings;
import org.springframework.util.ObjectUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.util.ContentCachingRequestWrapper;
import top.geeke.aop.CachingContentFilter;
import top.geeke.constants.AppConstPool;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

/**
 * <p>
 * 请求体工具类
 * </p>
 * @author 瞄你个汪
 * @since 2024/6/8 10:38
 */
@Slf4j
public class RequestUtils {
    /**
     * 默认隐式端口号
     */
    private static final Set<Integer> PORT_SET = new HashSet<Integer>() {{
        add(80);
        add(443);
    }};

    /**
     * 获取请求体
     * @return 请求体
     */
    public static HttpServletRequest getRequest() {
        try {
            return ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 读取请求体数据
     * @param request 请求体
     * @return 请求体数据
     */
    public static String readData(HttpServletRequest request) {
        if (!CachingContentFilter.FORM_CONTENT_TYPE.equalsIgnoreCase(request.getContentType())) {
            ContentCachingRequestWrapper wrapper = (ContentCachingRequestWrapper) request;
            return IOUtils.toString(wrapper.getContentAsByteArray(), StandardCharsets.UTF_8.name());
        }
        return null;
    }

    /**
     * 获取请求地址
     * @param request 请求体
     * @return 请求地址
     */
    public static String getRequestUrl(HttpServletRequest request) {
        String contentPath = request.getServletContext().getContextPath();
        int contentPathLength = contentPath.length();
        String target = request.getRequestURI();
        if (contentPathLength != 0) {
            target = target.substring(contentPathLength);
        }
        return target;
    }

    /**
     * 获取客户端的主机IP
     * @param request 请求体
     * @return 客户端主机IP
     */
    public static String getClientIP(HttpServletRequest request) {
        try {
            String ipAddress = request.getHeader("X-Forwarded-For");
            if (ipAddress == null) {
                ipAddress = request.getHeader("Proxy-Client-IP");
            }
            if (ipAddress == null) {
                ipAddress = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ipAddress == null) {
                ipAddress = request.getRemoteAddr();
            }

            // 如果请求头被伪造，可能会包含多个IP地址，这里通常取第一个
            if (ipAddress != null && ipAddress.indexOf(",") > 0) {
                ipAddress = ipAddress.split(",")[0].trim();
            }

            return ipAddress;
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 获取ip详情
     * @param ip 查询的IP
     * @return IP详细信息
     */
    public static String getClientAddress(String ip) {
        if (Strings.isBlank(ip)) ip = getClientIP(getRequest());
        String str = HttpUtil.get("http://ip.360.cn/IPQuery/ipquery?ip=" + ip);
        if (Strings.isBlank(str)) return null;
        JSONObject result = JSONUtil.parseObj(str);
        if (result.getInt("errno") == 0) {
            return result.getStr("data");
        }
        return null;
    }

    /**
     * 获取访问域地址，协议://域名:端口号
     * @return 域地址
     */
    public static String getDomain() {
        HttpServletRequest request = getRequest();
        if (request == null) return null;

        // 得到协议名 例如：http
        String scheme = request.getScheme();

        // 得到域名 localhost
        String serverName = request.getServerName();

        int port = request.getServerPort();
        if (!PORT_SET.contains(port)) return scheme + "://" + serverName + ":" + port;

        String domain = scheme + "://" + serverName;
        if (domain.contains("www.")) {
            domain = domain.replaceAll("www.", "");
        }
        return domain;
    }

    /**
     * 获取响应体
     * @return 响应体
     */
    public static HttpServletResponse getResponse() {
        try {
            return ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 向响应体写入数据
     * @param result 要写入的数据
     */
    public static void writeJsonToResponse(Object result) {
        HttpServletResponse response = getResponse();
        if (response == null) return;

        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");

        PrintWriter out;
        try {
            out = response.getWriter();
        } catch (IOException e) {
            throw new RuntimeException("向响应体写入json数据时发送异常", e);
        }
        out.print(result == null ? null : JSONUtil.toJsonStr(result));
        out.flush();
        out.close();
    }

    /**
     * 根据访问地址获取输入流
     * @param url 访问地址
     * @return 输入流
     */
    public static InputStream getInputStreamFromUrl(String url) {
        try {
            URL resource = new URL(url);
            URLConnection connection = resource.openConnection();
            return connection.getInputStream();
        } catch (Exception e) {
            log.error("获取流异常，请检查url是否正确：{}", e.getMessage());
        }
        return null;
    }

    /**
     * 获取cookie值
     * @param name cookie名
     * @return cookie值
     */
    public static String getCookieValue(String name) {
        return Optional.ofNullable(getCookie(name)).map(Cookie::getValue).orElse(null);
    }

    /**
     * 获取cookie
     * @param cookieName cookie名
     * @return Cookie对象
     */
    public static Cookie getCookie(String cookieName) {
        HttpServletRequest request = getRequest();
        if (request == null) return null;

        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if (cookie.getName().equals(cookieName)) {
                    return cookie;
                }
            }
        }
        return null;
    }

    /**
     * 清除 cookie
     * @param cookieName cookie名
     */
    public static void clearCookie(String cookieName) {
        Cookie cookie = getCookie(cookieName);
        HttpServletResponse response = getResponse();
        if (response == null) return;

        if (!ObjectUtils.isEmpty(cookie)) {
            cookie.setMaxAge(0); // 清除cookie
            cookie.setPath("/");
            response.addCookie(cookie);
        }
    }

    /**
     * 创建cookie
     * @param cookieName cookie名
     * @param value cookie值
     * @param maxAgeInSeconds 有效时长(秒)
     */
    public static void createCookie(String cookieName, String value, int maxAgeInSeconds) {
        HttpServletResponse response = RequestUtils.getResponse();
        if (response == null) return;

        Cookie cookie = new Cookie(cookieName, value);
        cookie.setHttpOnly(true);
        cookie.setPath("/");
        cookie.setMaxAge(maxAgeInSeconds);

        response.addCookie(cookie);
    }

    /**
     * 构建地址参数
     * @param map 参数map
     * @return query
     */
    public static String buildQueryUrl(Map<String, Object> map) {
        try {
            if (ObjectUtil.isEmpty(map)) return null;
            StringBuilder builder = new StringBuilder();
            for (String key : map.keySet()) {
                builder.append(key);
                builder.append("=");
                builder.append(URLEncoder.encode(map.getOrDefault(key, "").toString(), "UTF-8"));
                builder.append("&");
            }
            builder.delete(builder.length() - 1, builder.length());
            return builder.toString();
        } catch (Exception ex) {
            log.error("Url参数解析异常：{}", ex.getMessage());
        }
        return null;
    }

    /**
     * 获取文件资源可访问外链
     * @param path 文件相对地址
     * @return 外链
     */
    public static String getUploadPath(String path) {
        return String.format("%s%s?%s=%s", getDomain(), path, AppConstPool.AUTHORIZATION_PARAMETER_KEY, getToken());
    }

    /**
     * 获取token
     * @param request 请求体
     * @return token
     */
    public static String getToken(HttpServletRequest request) {
        String token = request.getHeader(AppConstPool.AUTHORIZATION_KEY);
        if (Strings.isBlank(token) && request.getRequestURI().startsWith("/upload/")) {
            token = request.getParameter(AppConstPool.AUTHORIZATION_PARAMETER_KEY);
        }
        return token == null ? null : token.replace(AppConstPool.AUTHORIZATION_SCHEMA_TYPE, "").trim();
    }

    /**
     * 获取token
     * @return token
     */
    public static String getToken() {
        return getToken(((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest());
    }

    /**
     * 根据 User-Agent 获取设备信息
     * @param request 请求体
     * @return 设备信息
     */
    public static String getDeviceInfo(HttpServletRequest request) {
        String userAgent = request.getHeader("User-Agent");
        if (Strings.isBlank(userAgent)) return "";

        UserAgent ua = UserAgentUtil.parse(userAgent);
        if (ua == null) return "";

        return String.format("%s(%s-%s)-%s_%s-%s_%s",
                ua.getPlatform().isMobile() ? "移动端" : "PC端",
                ua.getOs().getName(),
                ua.getOsVersion(),
                ua.getBrowser().getName(),
                ua.getVersion(),
                ua.getEngine().getName(),
                ua.getEngineVersion()
        );
    }
}
